package com.thilux.servicemanager.config;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.List;

import com.thilux.servicemanager.ServiceLocator;
import com.thilux.servicemanager.annotation.ManagedService;
import com.thilux.servicemanager.exception.ConfigFileNotFoundException;
import com.thilux.servicemanager.exception.ServiceAlreadyRegisteredException;
import com.thilux.servicemanager.manager.Service;
import com.thilux.servicemanager.util.reflection.PackageReflection;
import com.thilux.servicemanager.validator.ServiceConfigValidator;
import com.thilux.servicemanager.validator.ServiceValidator;
import com.thoughtworks.xstream.XStream;

public class Loader {

    private XStream xstream;

    public Loader() throws Exception{
	xstream = new XStream();
	xstream.alias("serviceConfig", com.thilux.servicemanager.config.ServiceConfig.class);
	xstream.alias("lookupLocation", com.thilux.servicemanager.config.LookupLocation.class);
	xstream.alias("serviceMapping", com.thilux.servicemanager.config.ServiceMapping.class);
	
	try {
	    if(ServiceConfigValidator.isValid()){
	        start();
	    }
	} catch (Exception e) {
	    throw e;
	}
    }
    
    private void start() throws ConfigFileNotFoundException{
	
	ServiceConfig serviceConfig = null;
	
	FileInputStream fis = null;
	try {
	    fis = new FileInputStream(Constants.SERVICE_CONFIG_FILE);
	} catch (FileNotFoundException e) {
	    throw new ConfigFileNotFoundException(Constants.SERVICE_CONFIG_FILE);
	}
	
	serviceConfig = (ServiceConfig) xstream.fromXML(fis);
	loadServiceFromScan(serviceConfig);
	loadServiceFromMapping(serviceConfig);

    }
    
    private void loadServiceFromScan(ServiceConfig serviceConfig){
	
	if(serviceConfig.getLookupLocations()==null || 
		serviceConfig.getLookupLocations().size()<=0){
	    
	    return;
	    
	}else{
	    
	    
	    for(LookupLocation location : serviceConfig.getLookupLocations()){
	
		PackageReflection packageReflection = new PackageReflection(location.getPackageName());
		List<Class<?>> classes = null;
		try{
		    classes = packageReflection.getClasses();
		}catch(Exception e){
		    
		    e.printStackTrace();
		}
		
		for(Class<?> clazz : classes){
		    
		    if(location.getNamePattern()!=null &&
			    !location.getNamePattern().equals("")){
			
			if (!clazz.getName().matches(location.getNamePattern())){
			    continue;
			}
			
		    }
		    
		    if(ServiceValidator.isClassAManagedService(clazz)){
			
			try {
			    ServiceLocator.registerService(getManagerServiceName(clazz), 
			    	clazz, location.getLoadType() == LoadType.EAGER);
			} catch (ServiceAlreadyRegisteredException e) {
			    // TODO Auto-generated catch block
			    e.printStackTrace();
			}
		    }
		}
	    }
	}
    }
    
    private void loadServiceFromMapping(ServiceConfig serviceConfig){

	if(serviceConfig.getServiceMappings()==null ||
		serviceConfig.getServiceMappings().size()<=0){
	    
	    return;
	    
	}else{
	    
	    for(ServiceMapping serviceMapping : serviceConfig.getServiceMappings()){
		
		Class<?> clazz = null;
		try {
		    clazz = Class.forName(serviceMapping.getServiceClass());
		} catch (ClassNotFoundException e) {
		    // TODO Auto-generated catch block
		    e.printStackTrace();
		}
		
		if(ServiceValidator.isClassAService(clazz)){
		    
		    try {
			ServiceLocator.registerService(serviceMapping.getServiceName(), 
			    clazz, serviceMapping.getLoadType() == LoadType.EAGER);
		    } catch (ServiceAlreadyRegisteredException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		    }
		    
		}
		
	    }
	    
	}
	
    }
    
    private String getManagerServiceName(Class<?> clazz){
	
	String serviceName = null;
	
	ManagedService annotation = clazz.getAnnotation(ManagedService.class);
	
	serviceName = annotation.name();
	
	if(serviceName.equals("")){
	    serviceName=clazz.getName();
	}
	
	return serviceName;
	
    }
}
